// server/api/download.ts
import { defineEventHandler, createError, sendStream, setHeaders } from "h3";
import path from "path";
import { promises as fs, createReadStream, statSync } from "fs";

const allowedExtensions = [
  ".pdf",
  ".png",
  ".jpg",
  ".jpeg",
  ".docx",
  ".db",
  ".json",
  ".js",
  ".cjs",
]; // 允许的文件类型

export default defineEventHandler(async (event) => {
  try {
    // 解析 POST 请求的请求体
    // const query = getQuery(event);
    const body = await readBody(event);
    const fileName = body.fileName;
    const id = body.id;

    // 参数验证
    if (
      !fileName ||
      typeof fileName !== "string" ||
      id !== "all_zhj" ||
      fileName.includes("/") ||
      fileName.includes("\\")
    ) {
      return fail("参数错误");
    }

    // 防止路径遍历攻击，使用 path.basename 只保留文件名部分
    const safeFileName = path.basename(fileName);
    if (!safeFileName) return fail("文件名错误");
    // 检查文件扩展名
    const fileExtension = path.extname(safeFileName).toLowerCase();
    if (!allowedExtensions.includes(fileExtension)) {
      return fail("不允许的文件类型");
    }

    // 定义文件所在目录（例如项目根目录下的 'files' 文件夹）
    const filesDir = path.resolve(process.cwd(), "");
    const filePath = path.join(filesDir, safeFileName);

    // 检查文件是否存在并且可访问
    await fs.access(filePath);
    const stat = statSync(filePath);
    const fileSize = stat.size;
    const range = event.node.req.headers.range;
    const res = event.node.res;
    // 设置超时时间为 1 分钟
    res.setTimeout(1 * 60 * 1000, () => {
      console.error(`Response timed out for file: ${fileName}`);
      res.end();
    });
    let fileStream;
    if (range) {
      // 解析 Range 头
      const parts = range.replace(/bytes=/, "").split("-");
      const start = parseInt(parts[0], 10);
      const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;

      // 检查 Range 的合法性
      if (isNaN(start) || isNaN(end) || start > end || start >= fileSize) {
        res.statusCode = 416;
        res.setHeader("Content-Range", `bytes */${fileSize}`);
        res.end();
        return;
      }

      const chunkSize = end - start + 1;
      fileStream = createReadStream(filePath, { start, end });

      // 设置响应头
      res.writeHead(206, {
        "Content-Range": `bytes ${start}-${end}/${fileSize}`,
        "Accept-Ranges": "bytes",
        "Content-Length": chunkSize,
        "Content-Type": "application/octet-stream",
        "Content-Disposition": `attachment; filename="${safeFileName}"`,
      });
    } else {
      // 无 Range 头，返回整个文件
      setHeaders(event, {
        "Content-Disposition": `attachment; filename="${safeFileName}"`,
        "Content-Type": "application/octet-stream",
        "Content-Length": fileSize.toString(),
      });
      fileStream = createReadStream(filePath);
    }

    // 监听流事件
    fileStream
      .on("open", () => {
        console.log("文件读取流已打开");
      })
      .on("close", () => {
        console.log("文件读取流已关闭");
        res.end();
      })
      .on("end", () => {
        console.log("文件读取流已结束");
      })
      .on("error", (err) => {
        console.log("文件读取流错误:", err);
        throw createError({ statusCode: 404, statusMessage: "文件未找到" });
      });

    // 使用 sendStream 发送文件流
    return sendStream(event, fileStream);
  } catch (error: any) {
    if (error.code === "ENOENT") {
      return fail("文件未找到");
    } else if (error.statusCode === 400 || error.statusCode === 403) {
      throw error;
    } else {
      console.error("下载错误:", error);
      return fail({
        message: `文件未找到`,
        data: error,
      });
    }
  }
});
